Skip to content

feat(hive): HiveSignOperations — parsed generic op signing (vote/post/custom_json)#307

Merged
BitHighlander merged 4 commits into
developfrom
feat/hive-sign-operations
Jul 15, 2026
Merged

feat(hive): HiveSignOperations — parsed generic op signing (vote/post/custom_json)#307
BitHighlander merged 4 commits into
developfrom
feat/hive-sign-operations

Conversation

@BitHighlander

Copy link
Copy Markdown
Owner

Implements P4 phase 1 from handoff-hive-sign-operations.md (review-clean revision): the host serializes the Graphene transaction; firmware parses the bytes, clear-signs the ops it recognizes, and refuses everything else — no blind-sign fallback. With #306 (login) this makes SkateHive fully usable: login → vote → post → snap; custom_json also covers Hive Engine and Splinterlands.

Stacked on #306 (feat/hive-sign-message), which stacks on #305. Merge order: #305#306 → this; GitHub retargets bases automatically.

Message types: 1616/1617

HiveSignOperations (address_n, chain_id ≤32, serialized_tx ≤2048) / HiveSignedOperations (signature). Digest is SHA256(chain_id ‖ serialized_tx) — identical to HiveSignTx, reusing the existing signing path, hashed straight from the decoded message (no 2KB stack copy).

Parser (phase-1 table: vote 0, comment 1, custom_json 18)

  • Bounded LEB128 (≤5 bytes, must fit uint32, overlong rejected), every string bounds-checked against the buffer and hived's field caps (names 1..16, permlinks 1..256, cj id 1..32, vote weight ∈ [-10000, 10000])
  • op_count 1..4, extensions must be empty, trailing bytes rejected — each with a specific Failure message
  • Op types 2/9/10 permanently excluded (rejected unconditionally, like unknown ops): transfer keeps the stronger dedicated HiveSignTx display path; account_create/update keep the device-derived-keys-only invariant — a generic raw-bytes path would let a host slip third-party authorities into an account_update
  • Everything displayed on the OLED is re-derived from the bytes being signed, so a host serializer bug can only produce a node rejection, never a silent wrong-sign

Role enforcement (dedicated validator — NOT the signBuffer one)

{posting', active'} only, pinned to the tx tier: vote/comment/cj-with-posting-auths → posting'; cj with required_auths → active'. memo' (legitimate for signBuffer, meaningless for Graphene ops) and owner' are rejected. Mixed-tier transactions are rejected as malformed — one signature cannot satisfy posting + active tiers post-HF28 — as is a custom_json carrying both auth arrays.

Display (uniform rules from the #306 finding-1 review)

One confirm per op + a final "Sign N Hive operation(s) with the {role} key?" confirm. Every truncated field carries an explicit (+N more) marker; any slice containing non-ASCII (comment bodies are arbitrary UTF-8) falls back to byte-count + hex preview instead of garbling partial multi-byte sequences. Vote weight shown with two decimals (50.50%, Downvote for negative).

Pins

  • deps/device-protocolf0b4549 (feat/hive-sign-operations, only the two new messages)
  • deps/python-keepkey15d95ec: hive.sign_operations(), wire mapping, pb2 regen (pinned protoc 3.5.1), and a test-owned Graphene mini-serializer so parser and serializer bugs can't cancel out

Verification

  • pyk on UDP emulator built from this branch: 28/28 hive tests — 19 from the base stack + 9 new: vote recovery, downvote + firmware-default chain id, unicode-body post (non-ASCII display path), custom_json posting + active, 2/9/10 exclusion, malformed structure (op_count 0/5, nonzero extensions, trailing bytes, overlong varint, weight range), role fences (active'/memo'/owner' on a posting tx, posting' on an active tx, mixed-tier, both-auths cj), oversize (2049+ → decode reject)
  • firmware-unit: 246/247 (sole failure is the documented Storage.StorageRoundTrip macOS-ARM64 golden artifact; Linux CI is authoritative)
  • Docker linux/amd64 make kkfirmware clean; zero pb_callback_t in generated nanopb
  • On-device OLED proof (Gate 3) pending CI/rc flash — the per-op confirm screens (vote %, post preview, custom_json id) are the ones to capture

Downstream (per the handoff, same-day once this lands)

hdwallet shim → vault POST /hive/sign-operations (+ ~80-line TS serializer) → Pioneer broadcast-ops (built, PR coinmastersguild/pioneer#170, deploy pending) → extension un-stubs requestVote/requestPost/requestCustomJson/requestBroadcast-subset. Definition of done: a KeepKey-signed vote on hiveblocks.com.

…g, unblocks vote/post/custom_json

P4 phase 1: the host serializes the Graphene transaction; firmware parses
the bytes, clear-signs the ops it recognizes (vote 0, comment 1,
custom_json 18), and refuses everything else. No blind-sign fallback.
Everything displayed is re-derived from the bytes being signed.

Parser (hive.c):
- bounded LEB128 (max 5 bytes, fits uint32, overlong rejected)
- field caps per hived: names 1..16, permlinks 1..256, cj id 1..32,
  vote weight int16 in [-10000,10000]
- op types 2/9/10 PERMANENTLY excluded: transfer keeps the dedicated
  HiveSignTx display path; account ops keep the device-derived-keys-only
  invariant (a raw-bytes path would let a host slip third-party
  authorities into an account_update)
- op count 1..4, extensions must be empty, trailing bytes rejected
- tier detection: vote/comment/cj-posting = posting; cj with
  required_auths = active; mixed tiers rejected as malformed (one
  signature cannot satisfy both post-HF28)

Handler (fsm_msg_hive.h):
- dedicated {posting', active'} path validator pinned to the tx tier
  (memo' and owner' rejected — no Graphene op uses them)
- one confirm per op + final sign confirm; uniform display rules:
  explicit (+N more) truncation markers, non-ASCII slices fall back to
  byte-count + hex preview
- digest SHA256(chain_id || serialized_tx) identical to HiveSignTx,
  hashed straight from the decoded message (no 2KB stack copy)
len==12 (header + op_count + extensions varints) now falls through to the
specific 'op count must be 1-4' error instead of the generic too-short
reject; op bodies are bounds-checked as they parse. Behavior-equivalent
for security — pure error-message accuracy.

Formatting: clang-format 20 (CI version) over the two files the lint job
flagged.
@BitHighlander BitHighlander force-pushed the feat/hive-sign-operations branch from ae9ff6e to 767276d Compare July 15, 2026 18:29
@BitHighlander BitHighlander changed the base branch from feat/hive-sign-message to develop July 15, 2026 19:39
@BitHighlander BitHighlander merged commit 183a2b9 into develop Jul 15, 2026
17 checks passed
@BitHighlander BitHighlander deleted the feat/hive-sign-operations branch July 15, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant